﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;

namespace AzureToolkit.Engine
{
    internal sealed class JobSchedulingProcess : IInitializer
    {
        private JobSchedulingSettings settings;
        private List<JobScheduleBase> schedules;
        private JobScheduler scheduler;

        private bool IsMaster
        {
            get
            {
                string id = RoleEnvironment.CurrentRoleInstance.Id;
                return id.EndsWith(".0") || id.EndsWith("_0");
            }
        }

        public JobSchedulingProcess(JobSchedulingSettings settings)
        {
            this.settings = settings;
            this.schedules = new List<JobScheduleBase>();
            this.scheduler = new JobScheduler(settings.Account, settings.QueueName);
        }

        public void AddScheduledJob<TJob>(JobSettings settings)
            where TJob : IJob
        {

            var schedule = CrontabSchedule.Parse(settings.CronSchedule);
            this.schedules.Add(new JobSchedule(schedule, typeof(TJob)));
        }

        public void AddRecurrentJob<TJob>(RecurrentJobSettings settings)
            where TJob : IJob
        {
            this.schedules.Add(new RecurrentJobSchedule(settings.RunEvery, typeof(TJob)));
        }

        public void Initialize()
        {
            this.scheduler.Initialize();
        }

        public void Run()
        {
            if (IsMaster)
            {
                Task.Factory.StartNew(() =>
                {
                    this.Cycle();
                });
            }
        }

        private void Cycle()
        {
            // Run...
            while (true)
            {
                NotifiyScheduledJobs(DateTime.UtcNow);
                Thread.Sleep(this.settings.SchedulingInterval);
            }
        }

        private void NotifiyScheduledJobs(DateTime currentTime)
        {
            foreach (var jobSchedule in schedules)
            {
                if (jobSchedule.ShouldRun(currentTime))
                {
                    Trace.WriteLine(String.Format("Running job {0}", jobSchedule.Type.AssemblyQualifiedName), "SchedulingProcess");
                    jobSchedule.Increment();
                    this.scheduler.TriggerJob(jobSchedule.Type);
                }
            }
        }


    }
}
